
import produce from 'immer'
import XLSX from 'xlsx'
import { errorToast, createAction, uniIndex } from 'Local/utils'
import config from 'Local/utils/config'

export const NAMESPACE = 'Index'
const currentState = _ => _[NAMESPACE]

const getEmptyLabelIndex = () => {
  const labelIndex = new Map()
  labelIndex.set(config.MAGIC.labelTest.title, {
    index: -1,
    must: true
  })
  labelIndex.set(config.MAGIC.labelTest.optionA, {
    index: -1,
    must: true
  })
  labelIndex.set(config.MAGIC.labelTest.optionB, {
    index: -1,
    must: true
  })
  labelIndex.set(config.MAGIC.labelTest.optionC, {
    index: -1,
    must: true
  })
  labelIndex.set(config.MAGIC.labelTest.optionD, {
    index: -1,
    must: true
  })
  labelIndex.set(config.MAGIC.labelTest.optionE, {
    index: -1,
    must: false
  })
  labelIndex.set(config.MAGIC.labelTest.optionF, {
    index: -1,
    must: false
  })
  labelIndex.set(config.MAGIC.labelTest.correct, {
    index: -1,
    must: true
  })
  labelIndex.set(config.MAGIC.labelTest.remarks, {
    index: -1,
    must: false
  })

  return labelIndex
}

const asyncFileReader = ({ file, onProgress }) => (
  new Promise((resolve, reject) => {
    const fileReader = new FileReader()
    fileReader.onload = () => {
      resolve(fileReader.result)
    }
    fileReader.onerror = reject
    if (onProgress) {
      fileReader.onprogress = evt => {
        const { loaded, total } = evt
        const progress = Math.round(loaded / total * 100) / 100
        onProgress(progress)
      }
    }

    fileReader.readAsArrayBuffer(file)
  })
)

export default {
  namespace: NAMESPACE,
  state: {
    loading: false,
    progress: 0,
    file: '',
    fileName: '',
    progessSyncHandle: () => {},

    questions: [],
    questionsTypeCount: {
      [config.MAGIC.questionType['判断题']]: 0,
      [config.MAGIC.questionType['单选题']]: 0,
      [config.MAGIC.questionType['多选题']]: 0
    },
    questionsProgess: 0,
    questionsScore: {
      right: 0,
      wrong: 0
    }
  },
  subscriptions: {
    setup ({ dispatch }) {
      const progessSyncHandle = progress => {
        dispatch(createAction('progessSyncHandle')({ progress }))
      }

      dispatch(createAction('patchFileProgess')({
        progessSyncHandle
      }))
    }
  },
  effects: {
    * fileChangeHandle ({ payload }, { select, put, call }) {
      const { file } = payload
      if (!file) {
        errorToast('文件为空')
        return
      }

      const { progessSyncHandle } = yield select(currentState)
      yield put(createAction('toggleLoading')({ loading: true }))

      try {
        const buffer = yield call(asyncFileReader, {
          file: file,
          onProgress: progessSyncHandle
        })
        const workbook = XLSX.read(buffer, { type: 'buffer' })

        // 这个地方需要做点嗅探逻辑
        // 1. 在第一行根据中文判断每一列是什么
        let questions = []
        let questionsTypeCount = {
          [config.MAGIC.questionType['判断题']]: 0,
          [config.MAGIC.questionType['单选题']]: 0,
          [config.MAGIC.questionType['多选题']]: 0
        }
        let labelIndex = getEmptyLabelIndex()

        workbook.SheetNames.forEach(sheetName => {
          /**
           * raw需要指定false因为excel的特性就是数据是一层，看到的是一层，当用了奇怪的格式就会拐，显示出来的raw是不能理解的
           */
          const roa = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { header: 1, raw: false })
          roa[0].forEach((label, index) => {
            labelIndex = produce(labelIndex, draft => {
              draft.forEach((value, key) => {
                if (value.index > -1) {
                  return
                }
                if (key.test(label)) {
                  value.index = index
                }
              })
            })
          })

          // 2. 依据 [1.] 的判断结果判定这个文件是否能识别到足够的题目、答案信息
          let thisSheetCanRun = true
          labelIndex.forEach(value => {
            if (value.index === -1 && value.must === true) {
              thisSheetCanRun = false
            }
          })

          // 3. 如果检查不通过就忽略这个表格
          if (!thisSheetCanRun) {
            throw new Error('未能识别出基础的的题目信息，请联系维护者进行适配')
          }

          // 4. 如果检查通过，那么开始循环填充、记录题目及答案信息
          let titleIndex = labelIndex.get(config.MAGIC.labelTest.title).index
          let optionAIndex = labelIndex.get(config.MAGIC.labelTest.optionA).index
          let optionBIndex = labelIndex.get(config.MAGIC.labelTest.optionB).index
          let optionCIndex = labelIndex.get(config.MAGIC.labelTest.optionC).index
          let optionDIndex = labelIndex.get(config.MAGIC.labelTest.optionD).index
          let optionEIndex = labelIndex.get(config.MAGIC.labelTest.optionE).index
          let optionFIndex = labelIndex.get(config.MAGIC.labelTest.optionF).index
          let correctIndex = labelIndex.get(config.MAGIC.labelTest.correct).index
          let remarksIndex = labelIndex.get(config.MAGIC.labelTest.remarks).index
          roa.forEach((row, rowIndex) => {
            if (rowIndex === 0) {
              // 第一行是表头
              return
            }
            const index = uniIndex()
            const title = row[titleIndex]
            const optionA = row[optionAIndex]
            const optionB = row[optionBIndex]
            const optionC = row[optionCIndex] || ''
            const optionD = row[optionDIndex] || ''
            const optionE = row[optionEIndex] || ''
            const optionF = row[optionFIndex] || ''
            const correct = row[correctIndex].trim().split('').filter(word => /\w/.test(word)).join(',').toUpperCase()
            const remarks = row[remarksIndex] || ''
            if (!title || !optionA || !optionB || !correct) {
              // 跳过，尽量保证可用
              return
            }

            /**
             * 题型判断
             * 1. 单选还是多选
             * 2. 单选的话判断有多少个选项，如果四个都有就是普通单选
             * 3. 否则就是判断题
             */
            let type = ''
            if (correct.length > 1) {
              type = config.MAGIC.questionType['多选题']
            } else if (optionC && optionD) {
              type = config.MAGIC.questionType['单选题']
            } else {
              type = config.MAGIC.questionType['判断题']
            }

            questionsTypeCount = produce(questionsTypeCount, typeConunt => {
              typeConunt[type] += 1
            })

            questions = produce(questions, draft => {
              draft.push({
                key: index,
                title,
                type,
                options: [
                  { key: 'A', selected: false, label: optionA },
                  { key: 'B', selected: false, label: optionB },
                  { key: 'C', selected: false, label: optionC },
                  { key: 'D', selected: false, label: optionD },
                  { key: 'E', selected: false, label: optionE },
                  { key: 'F', selected: false, label: optionF }
                ],
                correct,
                showCorrect: false,
                remarks
              })
            })
          })
        })

        yield put(createAction('updateParsedWorkbook')({
          questions,
          questionsTypeCount,
          fileName: file.name
        }))
      } catch (e) {
        yield put(createAction('toggleLoading')({ loading: false }))
        errorToast(e)
      }
    }
  },
  reducers: {
    toggleLoading (orevState, { payload }) {
      const { loading } = payload
      return produce(orevState, state => {
        state.loading = loading
      })
    },
    updateParsedWorkbook (prevState, { payload }) {
      const { questions, questionsTypeCount, fileName } = payload
      return produce(prevState, state => {
        state.loading = false
        state.file = ''
        state.progress = 0
        state.questionsProgess = 0
        state.questionsScore = {
          right: 0,
          wrong: 0
        }
        state.fileName = fileName
        state.questions = questions
        state.questionsTypeCount = questionsTypeCount
      })
    },
    patchFileProgess (prevState, { payload }) {
      const { progessSyncHandle } = payload
      return produce(prevState, state => {
        state.progessSyncHandle = progessSyncHandle
      })
    },
    optionSelectHandle (prevState, { payload }) {
      const {
        option: optionKey, question: questionKey
      } = payload
      return produce(prevState, state => {
        let index = 0
        let question
        for (;;) {
          question = state.questions[index]

          if (!question) {
            break
          }

          if (question.key === questionKey) {
            const isMulti = question.type === config.MAGIC.questionType['多选题']
            question.options.forEach(option => {
              if (option.key === optionKey) {
                if (isMulti) {
                  option.selected = !option.selected
                } else {
                  option.selected = true
                }
              } else if (!isMulti) {
                option.selected = false
              }
            })

            break
          }
          index += 1
        }
      })
    },
    wantAnswerHandle (prevState) {
      return produce(prevState, state => {
        const { questionsProgess } = state
        state.questions[questionsProgess].showCorrect = true
        const choosen = state.questions[questionsProgess].options.filter(option => !!option.selected).map(option => option.key).join(',')
        if (choosen === state.questions[questionsProgess].correct) {
          state.questionsScore.right += 1
        } else {
          state.questionsScore.wrong += 1
        }
      })
    },
    navQuestionHandle (prevState, { payload }) {
      const { current } = payload
      const { questionsProgess } = prevState
      return produce(prevState, state => {
        const targetQuestion = state.questions[questionsProgess]
        if (targetQuestion && !targetQuestion.showCorrect) {
          const choosenOption = targetQuestion.options.filter(option => !!option.selected)[0]
          if (choosenOption) {
            state.questions[questionsProgess].showCorrect = true
            if (choosenOption.key === state.questions[questionsProgess].correct) {
              state.questionsScore.right += 1
            } else {
              state.questionsScore.wrong += 1
            }
          }
        }

        state.questionsProgess = Number.parseInt(current)
      })
    }
  }
}
